#include "eth.h"
#include "ns.h"

static char   frame[ETH_BUF_LEN];
static struct ifreq ifr;
static char   macaddr[100];


/*
 * Given a 6 byte mac address, return a string representation
 */
char *
macaddr2str(char *mac)
{
    int  p = 0;

    memset(macaddr, 0, 100);
    p += sprintf(macaddr + p, "%02X:", mac[0]);
    p += sprintf(macaddr + p, "%02X:", mac[1]);
    p += sprintf(macaddr + p, "%02X:", mac[2]);
    p += sprintf(macaddr + p, "%02X:", mac[3]);
    p += sprintf(macaddr + p, "%02X:", mac[4]);
    p += sprintf(macaddr + p, "%02X ", mac[5]); 
   
    return macaddr;
}


/*
 * Initialize an interface for tx/rx'ing packets
 */
IF *
eth_interface_init(char *ifname, int dobind) 
{
    IF   *intf;
    int   rc;
    char *mac;
    int   uid;
 
    uid = getuid();
   
    /*
     * Allocate the interface handle
     */
    if (ifname == NULL) return NULL; 
    intf = (IF*) malloc(sizeof(IF));
    if (intf == NULL) return intf;

    memset(intf, 0, sizeof(IF));

    intf->name = ifname;

    /*
     * Set up a packet socket that will be used to communicate information 
     * about this interface to / from the kernel.  If we don't have supersuer
     * permissions, then we can't create the socket here; we have to get the
     * fd via IPC from a server
     */
    if (uid == 0) {
        intf->sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP));
    } else {
        intf->sock = netserver_get_packet_socket();
    }

    if (intf->sock < 0) {
        if (uid == 0) {
            fprintf(stderr, "ERROR: interface socket create error\n"); 
        } else {
            fprintf(stderr, "ERROR: could not get socket from netserver\n");
        }

        free(intf);
        return NULL;
    }

    /*
     * Initialize the ifreq structure that will be used to make ioctl calls
     */
    memset(&ifr, 0, sizeof(struct ifreq));
    strncpy(ifr.ifr_name, ifname, IFNAMSIZ);

    /*
     * Query the mac address for the interface
     */
    rc = ioctl(intf->sock, SIOCGIFHWADDR, &ifr); 
    if (rc != 0) {
        fprintf(stderr, "ERROR: interface mac addr query failed: %d\n", rc);
        free(intf);
        return NULL; 
    }
    memcpy(intf->mac, ifr.ifr_hwaddr.sa_data, ETH_MAC_LEN);

    /*
     * Query the ifindex for the interface
     */
    rc = ioctl(intf->sock, SIOCGIFINDEX, &ifr);
    if (rc != 0) {
        fprintf(stderr, "ERROR: interface ifindex query failed: %d\n", rc);
        free(intf);
        return NULL;
    }
    intf->index = ifr.ifr_ifindex;

    /*
     * Set up the socket address structure with the interface information
     */   
    memset(&intf->sockaddr, 0, sizeof(SSA));
    intf->sockaddr.sll_family = PF_PACKET;
    intf->sockaddr.sll_protocol = htons(ETH_P_IP);
    intf->sockaddr.sll_halen = ETH_MAC_LEN;
    intf->sockaddr.sll_ifindex = intf->index;
    memcpy(intf->sockaddr.sll_addr, intf->mac, ETH_MAC_LEN);


    /*
     * Binding is for rx only
     */
    if (dobind) {
       rc = bind(intf->sock, (SSA *) &intf->sockaddr, sizeof(SA));
       if (rc != 0) {
           fprintf(stderr, "ERROR: bind failed on interface '%s': %d\n", ifname, rc);
           free(intf);
           return NULL;
       }
    }

    return intf;
}


/*
 * Send a parcel of data out through an interface
 */
int 
eth_packet_send(void *data, int len, interface *intf) 
{
    return sendto(intf->sock, data, len, 0,
                 (SSA*) &intf->sockaddr, sizeof(SA));
}


/*
 * Recieve data from an interface.  Blocks until data arrives.
 */
int 
eth_packet_recv(void *data, int len, interface *intf) 
{
    int socklen;

    return recvfrom(intf->sock, data, len, 0, 0, &socklen);
}


/*
 * Build an IP packet
 */ 
void * 
eth_ip_packet(char *smac,  char *dmac, IP *sip, IP *dip, int proto)
{
    struct ether_header *eth;
    struct ip           *ip;

    eth = (struct ether_header *)(frame);
    ip = (struct ip *)(frame + ETH_HDR_LEN );

    /*
     * Set up the L2 fields
     */
    if (smac != NULL ) {
        memcpy(eth->ether_shost, smac, ETH_MAC_LEN);
    } else {
        memset(eth->ether_shost, 0, ETH_MAC_LEN);
    }

    if (dmac != NULL ) {
        memcpy(eth->ether_dhost, dmac, ETH_MAC_LEN);
    } else {
        memset(eth->ether_dhost, 0, ETH_MAC_LEN);
    }
    
    eth->ether_type = htons(ETH_P_IP);

    /*
     * Set up the L3 fields
     */
    ip->ip_hl         = 5;
    ip->ip_v          = 4;                           
    ip->ip_len        = htons(IP_HDR_LEN);                         
    ip->ip_ttl        = 255;                        
    ip->ip_p          = proto;
    ip->ip_src.s_addr = *sip;
    ip->ip_dst.s_addr = *dip;

    /*
     * TOOD: more can be done here - checksum, etc.
     */

    return (void*)frame;
}

/*
 * Given an ethernet frame, get the IP header
 */
int  
eth_packet_parse_ip(void *data, int len, struct ip *iphdr)
{
    struct ip *ip;
    

    ip = (struct ip*)(data + ETH_HDR_LEN);

    
    memcpy(iphdr, ip, sizeof(struct ip));


    return 0;
}


